要了解 React 除了基礎的概念,了解他們的發展史也是很重要的一環,整個的發展先後順序就如圖面一樣。
從圖面可知,本來的 React 就是透過 Javascript 去創造 virtual dom 節點的套件來運作而已。
那麼虛擬dom是怎麼產生的呢?
下面是用 Typescript 基本推倒的過程:
// 大概簡單的處理type -> html tag, props應該知道是什麼吧
interface VirtualElement {
type: string;
props: Props;
}
interface Props {
[key: string]: any;
children?: VirtualElement[] | string[];
}
// 你可以把它想像成React後面再幫我們處理的動作
class AnalysisUI {
private root: HTMLElement;
private virtualDom: VirtualElement | null;
constructor(rootSelector: string) {
this.root = document.querySelector(rootSelector) as HTMLElement;
this.virtualDom = null;
}
render(element: VirtualElement): void {
this.virtualDom = element;
this.update();
}
private update(): void {
if (this.virtualDom) {
const newDom = this.createDomElement(this.virtualDom);
this.root.innerHTML = '';
this.root.appendChild(newDom);
}
}
private createDomElement(virtualElement: VirtualElement): HTMLElement {
const { type, props } = virtualElement;
const dom = document.createElement(type);
// 這裡有透過chatGPT縮短過,可以直接套入html使用,跑起來是沒問題的
Object.keys(props)
.filter(prop => prop !== 'children')
.forEach(name => {
dom[name] = props[name];
});
if (props.children) {
props.children.forEach(child => {
if (child instanceof HTMLElement) {
dom.appendChild(child);
} else if (typeof child === 'string') {
dom.appendChild(document.createTextNode(child));
} else if (child) {
// 處理子層的子層
dom.appendChild(this.createDomElement(child));
}
});
}
return dom;
}
}
// 建立模擬 react 的 analysisUI
const analysisUI = new AnalysisUI('#app');
// 建立虛擬DOM元素,就是類似React.component會產出來的東西
// 然後再拿這個物件去做diff的處理
const appElement: VirtualElement = {
type: 'div',
props: {
className: 'app',
children: [
{ type: 'h1', props: { innerText: 'Hello, world!' } },
{ type: 'p', props: { innerText: '一定是大拇指啦!' } }
]
}
};
// 渲染虛擬DOM,可以把它想成是ReactDom在跑的那個步驟
analysisUI.render(appElement);
當然,這裡沒有仔細去處理效能優化的問題,只是讓大家理解虛擬DOM是怎麼產生的,如果直接套用的話就會得到虛擬dom產生的畫面,至於優缺點在你們剛開始學習React的時候都讀過了,我這裡就不再贅述。
在他們使用了jsx之後,讓我們可以將html的tag塞進js裡面,然後透過繼承React.component的方式去創造virtual dom的生命週期。
他的好處就是能透過{}
在html dom 節點當中自由切換使用 js,這樣的做法無疑是一大變革,在其他框架都必須用自己的 framework 語法處理 loop, if, … 的問題時(ex: v-for, v-if, *ngFor, *ngIf….),他提供了一個共用標準 『js語法』 ,也就是你js怎麼處理,你的html tag就怎麼處理,不用想說要用哪個語法來處理上述問題,{}
挖洞下去就是可以直接切成 javascript 語法,最後再透過babel轉譯為純 JavaScript。
// 我相信大家應該看過很多次了
const hello = <h1>Hello</h1>;
// 基本的挖洞
const name = "John";
const greeting = <p>Hello, {name}!</p>;
// 套在attr的使用
const handleClick = () => alert("晚安,瑪卡巴卡!");
const buttonElement = <button onClick={handleClick}>點擊</button>;
// React早期Class component
class MyClassComponent extends React.Component {
render() {
return (
<div>
<h1>早安,瑪卡巴卡!</h1>
</div>
);
}
}
// Functional component 這裡記住要大寫,大寫才會吃到React的生命週期
const MyFuncComponent = () => (<p>晚安,瑪卡巴卡</p>);
// 你可以在 JSX 中引用 React 元件,以組合和嵌套它們來構建複雜的 UI。
const element = (
<div>
<h1>Welcome to My App</h1>
<MyComponent />
</div>
);
這就是早期的class component盛行的作法,我也是經歷轉換期的洗禮,學了一圈hooks的教學後,找工作面試都在問你class component的問題,所以學新的技術真的是在為以後的工作鋪路。
再來的大變革就是現在普遍熟知的Hooks了,在 React 16.8 版本中,全面支持Hooks,Hooks 是一種新的 API,讓開發者可以在不使用 class component 的情況下使用狀態(state)和其他 React 功能,這種方式更簡潔、可讀性更高,並且使組件邏輯更容易共享,詳細的的演變可以參考React conf 2018,裡面有示範了很多class component如何替換成hooks來對應生命週期的。
今天的內容就到這裡,那麼接下來我們來嘗試理解幾個常用到的hooks。